//
//  GestureView.m
//  Playground
//
//  Created by Basuke on 08/03/24.
//  Copyright 2008 Basuke's. All rights reserved.
//

#import "GestureView.h"


/* Optional delegate methods support */
#ifndef __DELEGATE_IVAR__
#define __DELEGATE_IVAR__ _delegate
#endif
#ifndef __DELEGATE_METHODS_IVAR__
#define __DELEGATE_METHODS_IVAR__ _delegateMethods
#endif
#define TEST_DELEGATE_METHOD_BIT(__BIT__) (self->__DELEGATE_METHODS_IVAR__ & (1 << __BIT__))
#define SET_DELEGATE_METHOD_BIT(__BIT__, __NAME__) { if([self->__DELEGATE_IVAR__ respondsToSelector:@selector(__NAME__)]) self->__DELEGATE_METHODS_IVAR__ |= (1 << __BIT__); else self->__DELEGATE_METHODS_IVAR__ &= ~(1 << __BIT__); }

#define GESTUREVIEW_VERY_SHORT_PERIOD 0.3

// extended touch phase
enum {
	X_UITouchPhaseUndef = -1,
	X_UITouchPhaseMixed = -2
};

@interface UITouch(keyString)

- (NSString *)keyForDictionary;

@end

@interface GestureView(Local)

- (void)initEventState:(UIEvent *)event;

- (void)handleSingleTocuh:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)handleGestureTocuhes:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)invokeGesture;

- (UITouchPhase)touchPhaseOfAll:(NSSet *)touches;
- (BOOL)isVeryShortPeriod:(UIEvent *)event;

@end

@implementation GestureView

@synthesize delegate=_delegate;

- (id)initWithFrame:(CGRect)frame {
	if (self = [super initWithFrame:frame]) {
		[self setMultipleTouchEnabled:YES];
		
		_delegate = nil;
		_delegateMethods = 0;
		_currentTouches = [[NSMutableDictionary dictionaryWithCapacity:5] retain];
	}
	
	return self;
}

- (void)initEventState:(UIEvent *)event {
	_gestureStarted = NO;
	_touchMoved = NO;
	_handlingSingleTouch = nil;
	_firstTouchAt = event.timestamp;
}

- (void)touchesChangedWithEvent:(UIEvent *)event {
	NSSet *touches = event.allTouches;
	UITouchPhase aPhase = [self touchPhaseOfAll:touches];
	
	// Detecting the start of event
	if (aPhase == UITouchPhaseBegan) {
		[self initEventState:event];
	}
	
	if (!_gestureStarted && [touches count] == 1) {
		_handlingSingleTouch = [touches anyObject];
		[self handleSingleTocuh:_handlingSingleTouch withEvent:event];
	} else {
		if (!_gestureStarted) {
			_gestureStarted = YES;
		}
		
		if (_handlingSingleTouch) {
			if ([self isVeryShortPeriod:event]) {
				if(TEST_DELEGATE_METHOD_BIT(3))
					[_delegate gestureView:self touchCanceled:_handlingSingleTouch];
			} else {
				if(TEST_DELEGATE_METHOD_BIT(2))
					[_delegate gestureView:self touchEnded:_handlingSingleTouch];
			}
			_handlingSingleTouch = nil;
		}
		
		[self handleGestureTocuhes:touches withEvent:event];
		
		// Detecting the start of event
		if (aPhase == UITouchPhaseEnded) {
			[self invokeGesture];
		}
	}
}

- (void)handleSingleTocuh:(UITouch *)touch withEvent:(UIEvent *)event {
	switch (touch.phase) {
		case UITouchPhaseBegan:
			if(TEST_DELEGATE_METHOD_BIT(0))
				[_delegate gestureView:self touchBegan:touch];
			break;
			
		case UITouchPhaseMoved:
			if(TEST_DELEGATE_METHOD_BIT(1))
				[_delegate gestureView:self touchMoved:touch];
			_touchMoved = YES;
			break;
			
		case UITouchPhaseEnded:
			if (_touchMoved) {
				if(TEST_DELEGATE_METHOD_BIT(2))
					[_delegate gestureView:self touchEnded:touch];
			} else {
				if(TEST_DELEGATE_METHOD_BIT(3))
					[_delegate gestureView:self touchCanceled:touch];
				
				if (touch.tapCount == 1 && TEST_DELEGATE_METHOD_BIT(5)) {
					[_delegate gestureView:self tappedAt:[touch locationInView]];
				} else if(TEST_DELEGATE_METHOD_BIT(6)) {
					[_delegate gestureView:self tappedAt:[touch locationInView] count:touch.tapCount];
				}
			}
			
			break;
	}
}

- (void)handleGestureTocuhes:(NSSet *)touches withEvent:(UIEvent *)event {
	NSLog(@"%@", touches);
}

- (void)invokeGesture {
	
}
	
- (void)touchesCanceled {
	if(TEST_DELEGATE_METHOD_BIT(4))
		[_delegate gestureViewEventCanceled:self];
}

- (void)dealloc {
	[_currentTouches release];
	[super dealloc];
}

- (void) setDelegate:(id<GestureViewDelegate>)delegate {
	_delegate = delegate;
	
	SET_DELEGATE_METHOD_BIT(0, gestureView:touchBegan:);
	SET_DELEGATE_METHOD_BIT(1, gestureView:touchMoved:);
	SET_DELEGATE_METHOD_BIT(2, gestureView:touchEnded:);
	SET_DELEGATE_METHOD_BIT(3, gestureView:touchCanceled:);
	
	SET_DELEGATE_METHOD_BIT(4, gestureViewEventCanceled:);
	
	SET_DELEGATE_METHOD_BIT(5, gestureView:tappedAt:);
	SET_DELEGATE_METHOD_BIT(6, gestureView:tappedAt:count:);
}

// if touch phase of all of touches in the set are same, return that phase. otherwise return X_UITouchPhaseMixed

- (UITouchPhase)touchPhaseOfAll:(NSSet *)touches {
	UITouchPhase thePhase = X_UITouchPhaseUndef;
	
	for (UITouch *touch in touches) {
		if (thePhase == X_UITouchPhaseUndef) {
			thePhase = touch.phase;
		} else if (thePhase != touch.phase) {
			return X_UITouchPhaseMixed;
		}
	}
	
	return thePhase;
}

// if the event was happen in very short period of time since first touch

- (BOOL)isVeryShortPeriod:(UIEvent *)event {
	NSTimeInterval period = event.timestamp - _firstTouchAt;
	return (period < GESTUREVIEW_VERY_SHORT_PERIOD);
}

@end

